#pragma once
#include <carbon/defs.h>
#include <chrono>
#include <atomic>
#include <mutex>
#include <condition_variable>

CARBON_NS_BEGIN

/// @brief 同步栓锁。允许一个或多个线程等待一系列操作的完成。
class Latch final {
    CARBON_NONCOPYABLE(Latch)
    struct Data {
        std::atomic<size_t>     m_count;    
        std::mutex              m_lock;
        std::condition_variable m_cond;
        bool                    m_release {false}; // 用于辅助表示latch是否释放，因为m_count递减过程可能会溢出
    };
public:
    Latch() {}
    Latch(Latch &&other) : m_data(std::move(other.m_data)) {}
    Latch(size_t count) : m_data(new Data()) {
        m_data->m_count.store(count);
    }

    ~Latch() { }

    Latch & operator=(Latch &&other) {
        assert(m_data);
        if ( this == &other ) return *this;
        m_data = std::move(other.m_data);
        return *this;
    }

    void await() {
        std::unique_lock<std::mutex> lock(m_data->m_lock);
        Data * p = m_data.get();
        m_data->m_cond.wait(lock, [p] { return p->m_release; });    // 等待直到latch打开
    }

    void countDown(size_t n = 1) {
        size_t old = m_data->m_count.fetch_sub(n);
        if ( old <= n ) {
            std::lock_guard<std::mutex> lock(m_data->m_lock);
            m_data->m_release = true;       // latch打开
            m_data->m_cond.notify_all();
        }
    }

    size_t count() const { return m_data->m_count.load(); }

private:
    std::unique_ptr<Data> m_data;
}; // Latch

CARBON_NS_END
